/**
 * Copyright (C) 2012-2017 ZEEI Inc.All Rights Reserved.
 * 项目名称：common
 * 文件名称：FileUploadUtil.java
 * 包  名 称：com.zeei.common.file
 * 文件描述：文件上传工具类
 * 创建日期：2017年10月26日上午9:29:33
 *
 * 修改历史
 * 1.0 yuyunjian 2017年10月26日上午9:29:33 创建文件
 *
 */
package com.zeei.ems.base.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import org.apache.commons.io.FileUtils;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import javax.mail.internet.MimeUtility;
import com.zeei.ems.base.constant.GlobalConstant;
import com.zeei.ems.base.result.ResultBasicVO;
import org.apache.commons.lang3.StringUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;

import com.alibaba.fastjson.JSON;

/**
 * @类型名称：FileUploadUtil
 * @类型描述：文件上传工具类
 * @功能描述：提供文件下载方法
 * @创建作者：yuyunjian
 *
 */
public class FileUploadUtil {

    private static Logger logger = LoggerFactory
            .getLogger(FileUploadUtil.class);

    private final static String UPLOAD_FOLDER = GlobalConstant.UPLOAD_FOLDER;

    private static String generatedFileName = null;

    /**
     * 文件上传目录：全路径
     *
     * @param request
     * @return
     */
    public static String getUploadFolder(HttpServletRequest request, String path) {
        if (StringUtils.isEmpty(path)) {
            path = UPLOAD_FOLDER;
        }

        // content/img content/doc a/b/c
        String[] paths = path.split("/");
        String parent = "";
        for (int i = 0; i < paths.length; i++) {
            if (i == 0) {
                parent = request.getSession().getServletContext().getRealPath("/");
                // parent=WebAppContextUtil.FILE_ROOT + File.separator ;
            }
            parent = parent + paths[i] + File.separator;
            File folder = new File(parent);
            if (!folder.exists()) {
                folder.mkdir();
            }
        }
        return parent;
    }

    /**
     * 获取上传后的文件：全路径
     *
     * @param request
     * @param path
     *            上传路径，默认不提供填null
     * @param originFileName
     *            要上传的文件名
     * @return
     */
    public static File getUploadFile(HttpServletRequest request, String path,
                                     String originFileName) {
        String filepath = "";
        String newFileName = getFileName(originFileName);
        filepath += getUploadFolder(request, path) + File.separator
                + newFileName;
        generatedFileName = newFileName;
        return new File(filepath);
    }

    /**
     * 根据给定的文件名生成随机文件名用于上传文件命名
     *
     * @param originFileName
     * @return
     */
    public static String getFileName(String originFileName) {
        String suffix = getSuffix(originFileName);
        Format formatter = new SimpleDateFormat("yyyyMMdd-HHmmssS");
        StringBuffer buf = new StringBuffer();
        buf.append("F").append(formatter.format(new Date())).append(suffix);
        return buf.toString();
    }

    /**
     * @param fileName
     * @return
     * @Methodname: getSuffix
     * @Discription: 根据文件名获取文件后缀
     */
    public static String getSuffix(String fileName) {
        int index = fileName.lastIndexOf('.');
        String suffix = "";
        if (index != -1) {
            suffix = fileName.substring(index);
        }
        return suffix;
    }

    /**
     * 根据不同浏览器使用对应的编码,防止下载时显示的中文名乱码
     *
     */
    public static String createContentDisposition(HttpServletRequest request,
                                                  String name) throws IOException {
        String new_filename = URLEncoder.encode(name, "UTF8");
        String rtn = "";

        String userAgent = ((String) request.getHeader("USER-AGENT"))
                .toLowerCase(); // 用来判断使用的浏览器
        // IE浏览器，只能采用URLEncoder编码
        if (userAgent.indexOf("msie") != -1) {
            rtn = "filename=\"" + new_filename + "\"";
        }
        // Opera浏览器只能采用filename*
        else if (userAgent.indexOf("opera") != -1) {
            rtn = "filename*=UTF-8''" + new_filename;
        }
        // Safari浏览器，只能采用ISO编码的中文输出
        else if (userAgent.indexOf("safari") != -1) {
            rtn = "filename=\""
                    + new String(name.getBytes("UTF-8"), "ISO8859-1") + "\"";
        }
        // Chrome浏览器，只能采用MimeUtility编码或ISO编码的中文输出
        else if (userAgent.indexOf("applewebkit") != -1) {
            new_filename = MimeUtility.encodeText(name, "UTF8", "B");
            rtn = "filename=\"" + new_filename + "\"";
        }
        // FireFox浏览器，可以使用MimeUtility或filename*或ISO编码的中文输出
        else if (userAgent.indexOf("mozilla") != -1) {
            rtn = "filename*=UTF-8''" + new_filename;
        } else {
            rtn = "filename=\""
                    + new String(name.getBytes("UTF-8"), "ISO8859-1") + "\"";
        }

        return "attachment;" + rtn;
    }

    /**
     * @param response
     * @param inputStream
     *            需要下载的流
     * @param name
     *            下载所显示的名称
     * @Methodname: download
     * @Discription: 下载
     */
    public static void download(HttpServletResponse response,
                                HttpServletRequest request, InputStream inputStream, String name) {
        BufferedOutputStream bos = null;
        OutputStream fos = null;
        response.reset();
        try {
            response.setHeader("Content-disposition",
                    createContentDisposition(request, name));
            response.setContentType("application/octet-stream");

            fos = response.getOutputStream();
            bos = new BufferedOutputStream(fos);
//            IOUtils.copyLarge(inputStream, bos);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            try {
                bos.flush();
                inputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 下载网络文件
     */
    public static Object download(String filePath) {
        try {

            String fileName = FileUtil.getFileNameIncludeSuffix(filePath);// 文件名从路径取得
            byte[] bytes = FileUtils.readFileToByteArray(new File(filePath));
            HttpHeaders headers = new HttpHeaders();
            headers.setContentLength(bytes.length);
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setContentDispositionFormData(fileName, fileName);
            InputStreamResource resource = new InputStreamResource(
                    new ByteArrayInputStream(bytes));
            ResponseEntity<InputStreamResource> response = new ResponseEntity<InputStreamResource>(
                    resource, headers, HttpStatus.OK);
            return response;
        } catch (Exception e) {
            logger.error("文件下载失败!", e);
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.valueOf(MediaType.TEXT_HTML_VALUE
                    + ";charset=UTF-8"));
            ResultBasicVO result = new ResultBasicVO();
            result.setResult(false);
            result.setMessage(GlobalConstant.FILE_NOT_FOUND);
            return new ResponseEntity<String>(
                    JSON.toJSONString(result),
                    headers, HttpStatus.OK);
        }
    }

    /**
     * 下载文件通用接口
     *
     * @param filePath
     *            文件相对路径：全路径
     * @param fileName
     *            下载指定的文件名称，带扩展名
     * @param request
     * @return
     */
    public static Object download(String filePath, String fileName,
                                  HttpServletRequest request) {
        filePath = getDownloadPath(filePath, request);
        return downloadByPath(filePath, fileName, request);
    }

    /**
     * 下载指定文件
     */
	/*public static Object downloadAppoint(String filePath, String fileName) {
		try {
			byte[] bytes = FileUtils.readFileToByteArray(new File(filePath + File.separator + fileName));
			HttpHeaders headers = new HttpHeaders();
			headers.setContentLength(bytes.length);
			headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
			headers.setContentDispositionFormData(fileName, fileName);
			InputStreamResource resource = new InputStreamResource(new ByteArrayInputStream(bytes));
			ResponseEntity<InputStreamResource> response = new ResponseEntity<InputStreamResource>(resource, headers, HttpStatus.OK);
			return response;
		} catch (Exception e) {
			logger.error("文件下载失败!", e);
			HttpHeaders headers = new HttpHeaders();
			headers.setContentType(MediaType.valueOf(MediaType.TEXT_HTML_VALUE + ";charset=UTF-8"));
			return new ResponseEntity<String>(JSON.toJSONString(FileOperateStatusVO.FILE_NOT_FOUND), headers, HttpStatus.OK);
		}
	}*/

    /**
     * 指定路径下载，不需要拼装路径
     *
     * @param filePath
     *            全路径
     * @param fileName
     *            下载指定的文件名称，带扩展名
     * @param request
     * @return
     */
    public static Object downloadByPath(String filePath, String fileName,
                                        HttpServletRequest request) {
        HttpHeaders headers = new HttpHeaders();
        try {
            byte[] bytes = FileUtils.readFileToByteArray(new File(filePath));
            headers.setContentLength(bytes.length);
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.set(HttpHeaders.CONTENT_DISPOSITION,
                    FileUploadUtil.createContentDisposition(request, fileName));
            InputStreamResource resource = new InputStreamResource(
                    new ByteArrayInputStream(bytes));
            ResponseEntity<InputStreamResource> response = new ResponseEntity<InputStreamResource>(
                    resource, headers, HttpStatus.OK);
            return response;
        } catch (Exception e) {
            logger.error("文件下载失败!", e.toString());
            headers.setContentType(MediaType.valueOf(MediaType.TEXT_HTML_VALUE
                    + ";charset=UTF-8"));
            ResultBasicVO result = new ResultBasicVO();
            result.setResult(false);
            result.setMessage(GlobalConstant.FILE_NOT_FOUND);
            return new ResponseEntity<String>(
                    JSON.toJSONString(result),
                    headers, HttpStatus.OK);
        }
    }

    /**
     * 指定路径下载，不需要拼装路径
     *
     * @param filePath
     *            全路径
     * @param fileName
     *            下载指定的文件名称，带扩展名
     * @param request
     * @return
     */
    public static void downloadByPath(String filePath, String fileName, HttpServletRequest request, HttpServletResponse response) {
        File file = new File(filePath);
        if (file.exists()) {

            FileInputStream fileInputStream = null;
            BufferedInputStream inputStream = null;
            ServletOutputStream outputStream = null;
            try {
                // 设置文件头
                response.setHeader("Content-disposition",
                        createContentDisposition(request, fileName));
                response.setContentType("application/octet-stream");

                fileInputStream = new FileInputStream(file);
                inputStream = new BufferedInputStream(fileInputStream);

                outputStream = response.getOutputStream();
                byte[] b = new byte[1024];
                int len = -1;
                while ((len = inputStream.read(b)) != -1) {
                    outputStream.write(b, 0, len);
                }
                outputStream.flush();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (outputStream != null) {
                        outputStream.close();
                    }
                    if (inputStream != null) {
                        inputStream.close();
                    }
                    if (fileInputStream != null) {
                        fileInputStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static Object downloadExcel(HttpServletRequest request, HttpServletResponse response, String filePath, String fileName) {
        ByteArrayOutputStream out = null;
        BufferedOutputStream bos = null;
        InputStream ins = null;
        response.reset();
        try {
            byte[] bytes = FileUtils.readFileToByteArray(new File(filePath));
            out = new ByteArrayOutputStream();
            out.write(bytes);
            ins = new ByteArrayInputStream(out.toByteArray());
            response.setHeader("Content-disposition", FileUploadUtil.createContentDisposition(request, fileName));
            response.setContentType("application/vnd.ms-excel");

            bos = new BufferedOutputStream(response.getOutputStream());
//            IOUtils.copyLarge(ins, bos);
        } catch (Exception e) {
            logger.error("导出excel文件出错！" + e.toString());
        } finally {
            try {
                bos.flush();
                if (ins != null) {
                    ins.close();
                }
                if (bos != null) {
                    bos.close();
                }
                if (out != null) {
                    out.close();
                }
            } catch (Exception e) {
                logger.error("关闭流出错！" + e.toString());
            }
        }

        return null;
    }

    public static String getDownloadPath(String filePath,
                                         HttpServletRequest request) {
        // return WebAppContextUtil.FILE_ROOT + File.separator + filePath;
        return request.getSession().getServletContext().getRealPath("/") + filePath;
    }

    /**
     *
     * @param request
     * @param response
     * @param filepath
     * @return
     * @throws IllegalStateException
     * @throws IOException
     */
    public static ResultBasicVO upload(HttpServletRequest request,
                                       HttpServletResponse response, String filepath) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.valueOf(MediaType.TEXT_HTML_VALUE
                + ";charset=UTF-8"));

        ResultBasicVO result = new ResultBasicVO();

        // 创建一个通用的多部分解析器
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(
                request.getSession().getServletContext());
        // 判断 request 是否有文件上传,即多部分请求
        if (multipartResolver.isMultipart(request)) {
            // 转换成多部分request
            MultipartHttpServletRequest multiRequest = new StandardMultipartHttpServletRequest(request,false);
            // 取得request中的所有文件名
            Iterator<String> iter = multiRequest.getFileNames();
            while (iter.hasNext()) {
                // 记录上传过程起始时的时间，用来计算上传时间
                int pre = (int) System.currentTimeMillis();
                // 取得上传文件
                MultipartFile file = multiRequest.getFile(iter.next());
                if (file != null) {
                    // 取得当前上传文件的文件名称
                    String myFileName = file.getOriginalFilename();
                    // 如果名称不为“”,说明该文件存在，否则说明该文件不存在
                    if (StringUtils.isNotEmpty(myFileName)) {
                        // 重命名上传后的文件名
                        // 定义上传路径
                        File localFile = FileUploadUtil.getUploadFile(request,
                                filepath, myFileName);
                        try {
                            file.transferTo(localFile);
                        } catch (IllegalStateException e) {
                            e.printStackTrace();

                            result.setResult(false);
                            result.setMessage(GlobalConstant.ILLEGAL_ARGS);
                            return result;
                        } catch (IOException e) {
                            e.printStackTrace();
                            result.setResult(false);
                            return result;
                        }
                    }
                }
                // 记录上传该文件后的时间
                int finaltime = (int) System.currentTimeMillis();
                logger.info("file upload cost millseconds: "
                        + (finaltime - pre));
            }

        }

        result.setResult(true);
        result.setMessage(generatedFileName);
        return result;
        // return new
        // ResponseEntity<String>(JSON.toJSONString(FileOperateStatusVO.SUCCESS),
        // headers, HttpStatus.OK);
    }

    /**
     * 上传文件通用接口
     *
     * @param multipartFile
     *            springmvc
     * @param filepath
     * @param request
     * @param response
     * @return
     */
    public static ResultBasicVO upload(
            CommonsMultipartFile multipartFile, String filepath,
            HttpServletRequest request, HttpServletResponse response) {
        ResultBasicVO result = new ResultBasicVO();
        BufferedOutputStream bos = null;
        BufferedInputStream in = null;
        if (multipartFile != null && !multipartFile.isEmpty()) {
            int pre = (int) System.currentTimeMillis();
            try {

                // 拿到输出流，同时重命名上传的文件

                File file = FileUploadUtil.getUploadFile(request, filepath,
                        multipartFile.getOriginalFilename());

                // 新建文件输入流并对它进行缓冲
                in = new BufferedInputStream(multipartFile.getInputStream());

                // 新建文件输出流并对它进行缓冲
                bos = new BufferedOutputStream(new FileOutputStream(file));

                // 缓冲数组
                byte[] b = new byte[1024 * 5];
                int len;
                while ((len = in.read(b)) != -1) {
                    bos.write(b, 0, len);
                }
                // 刷新此缓冲的输出流
                bos.flush();
                int finaltime = (int) System.currentTimeMillis();
                logger.info("file upload cost millseconds: "
                        + (finaltime - pre));

            } catch (Exception e) {
                logger.error("上传出错" + e.toString());
                result.setResult(false);
                return result;
            } finally {
                if (bos != null) {
                    try {
                        bos.close();
                    } catch (IOException e) {
                        logger.error("关闭流出错" + e.toString());
                    }
                }
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        logger.error("关闭流出错" + e.toString());
                    }
                }
            }
        }
        result.setResult(true);
        result.setMessage(generatedFileName);
        return result;
    }


    /**
     *
     * download:(单文件下载).
     *
     * @param request
     * @param response
     * @param fileName
     *            文件名[如果为空就用url带的文件名,如果不为空,则用传进来的文件名(这个文件名是返回前端显示的文件名)]
     * @param fileSaveRootPath
     *            所有下载文件所存放的路径带文件名
     */
    public static String download(HttpServletRequest request, HttpServletResponse response, String fileName,
                                  String fileSaveRootPath) {
        FileInputStream in = null;
        OutputStream out = null;
        String message = "";
        try {
            File file = new File(fileSaveRootPath);

            if (StringUtils.isEmpty(fileName)) {
                fileName = file.getName();
            }

            if (!file.exists()) {
                return "您要下载的资源已被删除!";
            }
            if (request.getHeader("USER-AGENT").toLowerCase().indexOf("msie") > 0) {// IE
                fileName = URLEncoder.encode(fileName, "UTF-8");
                fileName = fileName.replace("+", "%20");// 处理空格变“+”的问题
            } else {// FF
                fileName = new String(fileName.getBytes("UTF-8"), "iso-8859-1");
            }
            response.setHeader("Content-Disposition", "attachment;filename=\"" + fileName + "\"");

            long fileSize = file.length();

            if (fileSize <= Integer.MAX_VALUE) {
                response.setContentLength((int) fileSize);
            } else {
                response.addHeader("Content-Length", Long.toString(fileSize));
            }

            // response.setContentLengthLong(fileSize);

            in = new FileInputStream(file);
            out = response.getOutputStream();
            byte buffer[] = new byte[1024];
            int len = 0;
            while ((len = in.read(buffer)) > 0) {
                out.write(buffer, 0, len);
            }
            out.close();
            in.close();

        } catch (Exception e) {
            logger.error("下载文件异常");
            logger.error("异常明细:", e);
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    logger.error("关闭输入流异常");
                    logger.error("异常明细:", e);
                }
            }
            if (out != null) {
                try {
                    out.flush();
                } catch (IOException e) {
                    logger.error("Flush输出流异常");
                    logger.error("异常明细:", e);
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    logger.error("关闭输出流异常");
                    logger.error("异常明细:", e);
                }
            }
        }
        return message;
    }


}
